<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="kfT0n" id="kfT0n"><span data-lake-id="u64fea570" id="u64fea570">典型回答</span></h1>
  <p data-lake-id="ub2a19766" id="ub2a19766"><span data-lake-id="u83b70eac" id="u83b70eac"> <br></span><span data-lake-id="u58a8edef" id="u58a8edef">数据倾斜是指在分布式计算或数据库环境中，数据分布不均匀的现象。在理想的分布式系统中，数据和计算负载应该均匀分布在所有节点上。然而，由于各种原因，某些节点可能承载比其他节点更多的数据或计算负载，这就是数据倾斜。</span></p>
  <p data-lake-id="u3dd2440b" id="u3dd2440b"><span data-lake-id="u6fd5369f" id="u6fd5369f">​</span><br></p>
  <p data-lake-id="u739a7e2a" id="u739a7e2a"><span data-lake-id="uf5d18ac5" id="uf5d18ac5">比如一个系统中共有500万数据，但是属于同一个商家的数据就有400万，那么如果数据库按照商家做分库分表，就会导致出现严重的数据倾斜。</span></p>
  <p data-lake-id="ufa9f708d" id="ufa9f708d"><span data-lake-id="uf82660dd" id="uf82660dd">​</span><br></p>
  <p data-lake-id="u5b11e7e6" id="u5b11e7e6"><span data-lake-id="u58bbac62" id="u58bbac62">一般来说，数据倾斜发生在分库分表的场景中比较多，因为主要是因为分表字段选择的不够合适导致的。你比如说我之前做过一个定价系统，然后我们是按照付款方做的分库分表。原来都挺好的，同一个用户的付款也不会有什么特别多的数据，但是后来出现了一种机构付款的情况，有一个商户作为付款方了，那么就会导致这个商户的数据有很多，就会大量的堆积在同一张表中，就导致这个表数据量剧增。</span></p>
  <p data-lake-id="u5f13c4b7" id="u5f13c4b7"><span data-lake-id="uc701e41c" id="uc701e41c">​</span><br></p>
  <p data-lake-id="u1424ed64" id="u1424ed64"><span data-lake-id="uce72f6ce" id="uce72f6ce">数据倾斜主要会带来以下几个问题：</span></p>
  <p data-lake-id="uc66a716f" id="uc66a716f"><br></p>
  <ol list="ucaa4b18c">
   <li fid="uc3cef8db" data-lake-id="u96dde767" id="u96dde767"><span data-lake-id="u522f3f40" id="u522f3f40">性能瓶颈：数据倾斜可能导致某些节点的负载明显高于其他节点，从而成为整个系统的性能瓶颈。</span></li>
  </ol>
  <ul list="u914ee8fe">
   <li fid="u78f8c9b0" data-lake-id="ua42e5518" id="ua42e5518"><span data-lake-id="ud97de94c" id="ud97de94c">比如在Redis中，出现的热key问题，其实也是数据倾斜的一种具体情况，那么就会导致这个节点的负载非常高。</span></li>
  </ul>
  <p data-lake-id="uec5f08c7" id="uec5f08c7"><br></p>
  <ol list="ucaa4b18c" start="2">
   <li fid="uc3cef8db" data-lake-id="uf2cabb3e" id="uf2cabb3e"><span data-lake-id="u42b31517" id="u42b31517">资源利用不均</span><span data-lake-id="u5a548c4e" id="u5a548c4e">：导致资源利用不均衡，一部分节点可能过载，而其他节点则闲置。</span></li>
  </ol>
  <p data-lake-id="ue1b1b5cf" id="ue1b1b5cf"><br></p>
  <ol list="ucaa4b18c" start="3">
   <li fid="uc3cef8db" data-lake-id="u51020eea" id="u51020eea"><span data-lake-id="ub95b3f2d" id="ub95b3f2d">查询效率低下：在数据库中，数据倾斜可能导致查询效率低下，特别是在执行JOIN操作或聚合查询时。</span></li>
  </ol>
  <ul list="uf5e26d40">
   <li fid="u9219c29c" data-lake-id="u06b47824" id="u06b47824"><span data-lake-id="u45003956" id="u45003956">比如数据库分表后数据倾斜，就会使得分表的效果并不明显，单表的数据量还是可能会很大，导致查询速度变慢。</span></li>
  </ul>
  <p data-lake-id="u99962e8a" id="u99962e8a"><br></p>
  <ol list="ucaa4b18c" start="4">
   <li fid="uc3cef8db" data-lake-id="u926a7e13" id="u926a7e13"><span data-lake-id="u42c91d45" id="u42c91d45">影响其他业务：某些数据倾斜会导致查询比较慢，这样不仅使这个业务有影响，和他在同一张表中的其他业务的数据也会有影响。</span></li>
  </ol>
  <ul list="u62efd23d">
   <li fid="u9219c29c" data-lake-id="ucc51e045" id="ucc51e045"><span data-lake-id="ue843e206" id="ue843e206">比如某个小商家和一个大商户的数据在同一张表，那么它的查询也会变慢。</span></li>
  </ul>
  <p data-lake-id="u3326d8ae" id="u3326d8ae"><br></p>
  <p data-lake-id="u4634090f" id="u4634090f"><span data-lake-id="ub0999d4a" id="ub0999d4a">所以，我们在做分库分表的时候，在选择分表字段的时候，一定要考虑数据倾斜的问题，尽量选择那种不会有聚集性数据的字段来做分表字段，如订单表，尽量选择买家ID。</span></p>
  <p data-lake-id="ue201d10a" id="ue201d10a"><br></p>
  <h3 data-lake-id="By1ut" id="By1ut"><span data-lake-id="u5a765eaf" id="u5a765eaf">如何解决</span></h3>
  <p data-lake-id="ufd80d49b" id="ufd80d49b"><br></p>
  <p data-lake-id="u44810064" id="u44810064"><span data-lake-id="u6d20cde0" id="u6d20cde0">要解决数据倾斜的问题，其实和解决单表数据量大是同样的方案，无非就是把数据在拆分一下，分散数据量。</span></p>
  <p data-lake-id="ued21ae9c" id="ued21ae9c"><span data-lake-id="ud761d0f7" id="ud761d0f7">​</span><br></p>
  <h4 data-lake-id="jgJX5" id="jgJX5"><span data-lake-id="uc5c2dc07" id="uc5c2dc07">拆分</span></h4>
  <p data-lake-id="uc3bf2329" id="uc3bf2329"><span data-lake-id="u3587b5d7" id="u3587b5d7">比如在Redis中，为了解决热key的问题，可以采用Cluster模式，把一个大key拆分到多个实例上存储。</span></p>
  <p data-lake-id="ua95c639a" id="ua95c639a"><span data-lake-id="u08d4e83f" id="u08d4e83f">​</span><br></p>
  <p data-lake-id="u27cbcc9e" id="u27cbcc9e"><br></p>
  <p data-lake-id="ucfc8e5fe" id="ucfc8e5fe"><span data-lake-id="u4f5d40d3" id="u4f5d40d3">还有就是在分库分表场景，那就是做一下二次分表。</span></p>
  <p data-lake-id="u5123d6de" id="u5123d6de"><span data-lake-id="ua1106cba" id="ua1106cba">​</span><br></p>
  <p data-lake-id="u11cb4805" id="u11cb4805"><span data-lake-id="u007a24a0" id="u007a24a0">比如我之前提的那个定价记录表付款方严重倾斜的问题，为了解决它，我修改了一下我的路由算法。如果付款方式机构类型，那么我在非表时就会把时间因素考虑进去。基于时间在做一次分表。</span></p>
  <p data-lake-id="u3afd9c39" id="u3afd9c39"><span data-lake-id="uf2adba04" id="uf2adba04">​</span><br></p>
  <pre lang="java"><code>
//为了解决机构账户的热点问题，这里对于付款方是机构的情况做特殊处理，拼接时间戳后进行分表
switch (customerTypeEnum) {
    case INSTITUTION_NAME:
        externalId = quotationDO.getPayerId() +  DateUtils.truncate(quotationDO.getBizTime(), Calendar.SECOND).getTime();
        break;
    default:
        externalId = quotationDO.getPayerId();
}

//这里再基于externalId做哈希取模
</code></pre>
  <p data-lake-id="uc2113bdb" id="uc2113bdb"><span data-lake-id="uf6b2b972" id="uf6b2b972">​</span><br></p>
  <p data-lake-id="udc1d103c" id="udc1d103c"><span data-lake-id="ufbe0aa0a" id="ufbe0aa0a">或者如果付款方一样，那么就基于收款方再做一次分表。这样后续每一次的插入、查询、更新都用同一个分表算法就行了。算法内部自己把这个逻辑包掉就可以了。</span></p>
  <p data-lake-id="u1d76423b" id="u1d76423b"><span data-lake-id="u6d03b1ff" id="u6d03b1ff">​</span><br></p>
  <p data-lake-id="u97dca430" id="u97dca430"><span data-lake-id="u8b4bf034" id="u8b4bf034">只不过在查询的时候，就需要的信息就会多一些，以前可能根据一个付款方就够查询了，因为只需要这一个字段就能知道会路由到哪张表，但是修改之后，就需要付款方、付款方类型以及时间戳三个字段了。就会使得查询更加麻烦一些，当然也不是所有查询都需要， 只有热点账户相关的才需要，非热点的并不需要。</span></p>
  <p data-lake-id="u16bca239" id="u16bca239"><span data-lake-id="u6460daa9" id="u6460daa9">​</span><br></p>
  <h4 data-lake-id="OA1DP" id="OA1DP"><span data-lake-id="u2ca2d6e8" id="u2ca2d6e8">隔离</span></h4>
  <p data-lake-id="u588c3850" id="u588c3850"><br></p>
  <p data-lake-id="u0cf8e72b" id="u0cf8e72b"><span data-lake-id="u14b03ec0" id="u14b03ec0">除了做二次分表或者数据拆分以外。还有一个办法，为了降低因为数据倾斜而带来的影响，有的时候我们也会采取物理隔离的方式。</span></p>
  <p data-lake-id="u30d84bc2" id="u30d84bc2"><span data-lake-id="ub1c8ac79" id="ub1c8ac79">​</span><br></p>
  <p data-lake-id="u1fde7109" id="u1fde7109"><span data-lake-id="uc0298803" id="uc0298803">所谓物理隔离，其实就是把这个严重倾斜的商户的数据，单独独立出来放到一个单独的数据库中。</span></p>
  <p data-lake-id="ue843a04e" id="ue843a04e"><span data-lake-id="u7e1b3200" id="u7e1b3200">​</span><br></p>
  <p data-lake-id="u1e63116e" id="u1e63116e"><span data-lake-id="udcfff583" id="udcfff583">这样既可以降低对其他用户的影响，又可以单独给这个数据库增加配置，提升可用性。</span></p>
  <p data-lake-id="u301ddde7" id="u301ddde7"><span data-lake-id="ucffadb69" id="ucffadb69">​</span><br></p>
  <p data-lake-id="u9d7916c7" id="u9d7916c7"><span data-lake-id="uc1942b89" id="uc1942b89">这样做只不过会带来一些额外的成本，并且需要应用在数据库访问的时候动态决策具体去哪个表中做查询，在分表算法中需要做一些定制逻辑。</span></p>
 </body>
</html>